package com.hwtx.form.controller;

import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.collect.Lists;
import com.hwtx.form.annotation.Log;
import com.hwtx.form.annotation.OperationType;
import com.hwtx.form.domain.def.meta.Api;
import com.hwtx.form.domain.def.meta.Button;
import com.hwtx.form.domain.def.meta.Component;
import com.hwtx.form.domain.def.meta.Style;
import com.hwtx.form.domain.repo.ApiRepo;
import com.hwtx.form.domain.service.CommonApiInvoker;
import com.hwtx.form.persistence.entity.ApiDef;
import com.hwtx.form.util.FormUtil;
import com.hwtx.form.util.JacksonUtil;
import com.hwtx.form.util.Param;
import io.geekidea.boot.config.properties.BootProperties;
import io.geekidea.boot.framework.response.ApiCode;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.hwtx.form.domain.FormConstants.*;
import static com.hwtx.form.domain.service.CommonApiInvoker.getSelectResult;

@Slf4j
@RestController
@RequestMapping(COMMON_URL)
public class ApiController {

    @Resource
    private ApiRepo apiRepo;
    @Resource
    private BootProperties bootProperties;
    @Resource
    private CommonApiInvoker commonApiInvoker;
    @Resource
    private ServerProperties webApplicationContext;

    @GetMapping("/load/test/{id}")
    @Operation(summary = "获取测试表单")
    @Log(type = OperationType.GET)
    public String test(@PathVariable Long id) throws Exception {
        ApiDef apiDef = apiRepo.getById(id);
        Component.Form form = new Component.Form();
        form.setId("testForm");
        form.setCanAccessSuperData(false);
        List<String> paramValues = Lists.newArrayList();
        List<Component> body = Lists.newArrayList();
        if (StringUtils.isNotBlank(apiDef.getParams())) {
            List<Param> params = JacksonUtil.json2Array(apiDef.getParams(), new TypeReference<>() {
            });
            if (params != null) {
                params.forEach(param -> {
                    if (param.getConditions() != null) {
                        for (Param.Condition condition : param.getConditions()) {
                            if (OperatorType.of(condition.getCOperator()) == OperatorType.ANY) {
                                return;
                            }
                        }
                    }
                    paramValues.add("${" + param.getName() + "}");
                    Component.Input paramInput = new Component.Input();
                    paramInput.setFormBatProp();
                    paramInput.setType(Component.INPUT_TEXT);
                    paramInput.setLabel(param.getName());
                    if (BooleanUtils.toBoolean(param.getRequired())) {
                        paramInput.setRequired(true);
                    }
                    paramInput.setName(param.getName());
                    body.add(paramInput);
                });
            }
        }
        Api api = CommonApiInvoker.getApiUrlWithValues(apiDef, bootProperties.getDomain(), FormUtil.getUrlContext(webApplicationContext), paramValues);
        body.addFirst(getInput(api));
        Button button = new Button();
        button.setLabel("测试");
        button.setLevel("primary");
        Component.OnEvent onEvent = new Component.OnEvent();
        Component.ClickEvent clickEvent = new Component.ClickEvent();
        String url = api.getUrl();
        Map<String, Object> data = new HashMap<>();
        if (Objects.equals(api.getMethod(), HTTP_POST)) {
            if (api.getData() != null) {
                data.putAll(api.getData());
            } else {
                data.put("test", true);
            }
        } else {
            if (url.contains("?")) {
                url += "&test=true";
            } else {
                url += "?test=true";
            }
        }
        clickEvent.setActions(Lists.newArrayList(
                Component.Action.getValidateAction("testForm", null),
                Component.Action.getStopPropagationAction("${!ISEMPTY(event.data.validateResult.error)}"),
                Component.Action.getAjaxAction(Api.builder().method(api.getMethod()).url(url).data(data).build(), "myResult"),
                Component.Action.getSetValueAction("${myResult}", "ret")
        ));
        onEvent.setClick(clickEvent);
        button.setOnEvent(onEvent);
        body.add(button);
        Component.Editor editor = new Component.Editor();
        editor.setId("ret");
        editor.setName("editor");
        editor.setLanguage("json");
        editor.setLabel("返回结果");
        Style style = new Style();
        style.put("margin-top", "10px");
        editor.setStyle(style);
        body.add(editor);
        form.setBody(body);
        return JacksonUtil.object2json(form);
    }

    private Component.@NotNull Input getInput(Api api) {
        Component.Input input = new Component.Input();
        input.setType(Component.INPUT_TEXT);
        input.setFormBatProp();
        input.setLabel("URL");
        input.setValue(api.getUrl());
        input.setDisabled(true);
        return input;
    }

    @GetMapping("/api/{apiCode}/{retType}")
    @Operation(summary = "返回api接口数据")
    @Log(type = OperationType.GET, value = "返回select结构的api")
    public Api2Result invoke(@PathVariable String apiCode, @PathVariable Integer retType, HttpServletRequest request) {

        if (StringUtils.isEmpty(apiCode)) {
            return Api2Result.fail("请求编码不能为空");
        }
        if (retType == null) {
            return Api2Result.fail("返回类型不能为空");
        }
        ApiRetType apiRetType = ApiRetType.of(retType);
        if (apiRetType == null) {
            return Api2Result.fail("返回类型错误");
        }
        log.info("apiCode: {}, params: {}", apiCode, JacksonUtil.object2json(request.getParameterMap()));
        CommonApiInvoker.InvokeData invokeData = commonApiInvoker.invokeApi(apiCode, request.getParameterMap());
        if (invokeData == null) {
            return Api2Result.result(ApiCode.SUCCESS, "执行成功", "");
        }
        if (invokeData.isExtInvoke()) {
            return Api2Result.result(ApiCode.SUCCESS, "执行成功", invokeData.getRetData());
        }
        if (apiRetType.equals(ApiRetType.Chained_Select) || apiRetType.equals(ApiRetType.Select)) {
            return Api2Result.result(ApiCode.SUCCESS, "执行成功", getSelectResult(invokeData));
        } else if (apiRetType.equals(ApiRetType.Select_Label)) {
            List<String> ret = Lists.newArrayList();
            invokeData.getRet().forEach(item -> ret.add(item.get(invokeData.getSelectItems().getFirst()).toString()));
            return Api2Result.result(ApiCode.SUCCESS, "执行成功", ret);
        } else if (apiRetType.equals(ApiRetType.GROUP_SELECT_CHAIN)) {
            String name = "chain-" + apiCode;
            List<Map<String, String>> ret = invokeData.getRet().stream().map(row -> Map.of(name, invokeData.getSelectItems().stream().map(item -> {
                if (row.get(item) != null) {
                    return row.get(item).toString();
                }
                return "";
            }).collect(Collectors.joining(",")))).toList();
            return Api2Result.result(ApiCode.SUCCESS, "执行成功", ret);
        } else if (apiRetType.equals(ApiRetType.Map)) {
            if (!invokeData.getRet().isEmpty()) {
                return Api2Result.result(ApiCode.SUCCESS, "执行成功", invokeData.getRet().getFirst());
            }
            return Api2Result.result(ApiCode.SUCCESS, "执行成功", "");
        } else if (apiRetType.equals(ApiRetType.LIST)) {
            return Api2Result.result(ApiCode.SUCCESS, "执行成功", invokeData.getRet());
        }
        return Api2Result.fail("暂不支持该类型返回");
    }

    @GetMapping("/chart/{type}")
    @Operation(summary = "返回echarts图形配置")
    @Log(type = OperationType.GET, value = "返回echarts图形配置")
    public String invoke(@PathVariable String type) {
        try {
            String content = FileUtils.readFileToString(new File(ClassLoader.getSystemResource("chartConfig.json").getPath()), StandardCharsets.UTF_8);
            return Api2Result.build(ApiCode.SUCCESS, "加载成功", content);
        } catch (IOException e) {
            return Api2Result.build(ApiCode.FAIL, "加载图形配置失败", "");
        }
    }
}
